//////////////////////////////////////////////////////////////////////
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
//  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// This code is released under the Microsoft Public License.  Please
// refer to LICENSE.TXT for the full text of the license.
//
// Copyright 2012 Guan-Tik Tiunn.  All rights reserved.
// Portions Copyright 2003 Microsoft Corporation.  All rights reserved.
//
// Adapted from the Text Services Framework Sample Code, available under
// the Microsoft Public License from:
//    http://code.msdn.microsoft.com/tsf
//////////////////////////////////////////////////////////////////////


#include "Globals.h"
#include "TextService.h"
#include "EditSession.h"
#include <ctime>


// {FE22A4F3-74D8-45D5-93D0-789F5A08EAC4}
static const GUID GUID_PRESERVEDKEY_COMMON = 
{ 0xfe22a4f3, 0x74d8, 0x45d5, { 0x93, 0xd0, 0x78, 0x9f, 0x5a, 0x8, 0xea, 0xc4 } };



// {28391C9D-FA0E-4B1B-9284-748E665803A7}
static const GUID GUID_PRESERVEDKEY_PERIOD = 
{ 0x28391c9d, 0xfa0e, 0x4b1b, { 0x92, 0x84, 0x74, 0x8e, 0x66, 0x58, 0x3, 0xa7 } };


/* Set Ctrl+, as on off key */
//static const TF_PRESERVEDKEY c_pkeyOnOff0 = { 0xC0, TF_MOD_CONTROL };//0xC0 == ~
static const TF_PRESERVEDKEY c_pkeyCommon = { VK_OEM_COMMA, TF_MOD_CONTROL };//0xC0 == ~

/* Set Ctrl+. as on off key */
static const TF_PRESERVEDKEY c_pkeyPeriod = { VK_OEM_PERIOD, TF_MOD_CONTROL };//0xC0 == ~

// the description for the preserved keys
static const WCHAR c_szPKeyCommon[] = L"Common";
static const WCHAR c_szPKeyPeriod[] = L"Period";


LPARAM globalparam;


clock_t clocker_for_shift;// = clock();
//+---------------------------------------------------------------------------
//
// _IsKeyEaten
//
//----------------------------------------------------------------------------

BOOL CTextService::_IsKeyEaten(WPARAM wParam, LPARAM lParam)
{
	globalparam = lParam;
	#ifdef _DEBUG
	DebugPrintf("BOOL CTextService::_IsKeyEaten(WPARAM wParam, LPARAM lParam)");
    #endif
	// Let control chracters through
	BYTE state[256];
	GetKeyboardState(state);
	if (state[VK_CONTROL] & 0x80) return FALSE;
	if (state[VK_MENU] & 0x80) return FALSE;

	// if the keyboard is disabled, the keys are not consumed.
	if (_IsKeyboardDisabled())
		return FALSE;

	// if the keyboard is closed, the keys are not consumed.
	if (!_IsKeyboardOpen())
		return FALSE;
	if (wParam>=0x61 && wParam<=0x69 && _IsComposing())
	{
		switch (wParam)
		{
			case 0x61:
				wParam = 0x31;
				return TRUE;
			case 0x62:
				wParam = 0x32;
				return TRUE;			
			case 0x63:
				wParam = 0x33;
				return TRUE;
			case 0x64:
				wParam = 0x34;
				return TRUE;
			case 0x65:
				wParam = 0x35;
				return TRUE;
			case 0x66:
				wParam = 0x36;
				return TRUE;
			case 0x67:
				wParam = 0x37;
				return TRUE;
			case 0x68:
				wParam = 0x38;
				return TRUE;
			case 0x69:
				wParam = 0x39;
				return TRUE;
		}
		return FALSE;
	}
	//eat phone key
	if ((wParam >= 0x30 && wParam <= 0x39)||
		(wParam >= 0x41 && wParam <= 0x5A)||(wParam==VK_OEM_1)||
		(wParam==VK_OEM_2)||(wParam==VK_OEM_PERIOD)||(wParam==VK_OEM_COMMA))
	{
			return TRUE;
	}
	// eat only keys that CKeyHandlerEditSession can hadles.
	switch (wParam)
	{
		/*
	case VK_MENU:
		POJ_enable=1;
		return TRUE;
		
	case VK_LEFT:
	case VK_RIGHT:
	case VK_RETURN:
	case VK_BACK:
	case VK_SPACE:
	case VK_DOWN:
	case VK_UP:
	case VK_TAB:
	case VK_HOME:
	case VK_END:
	case VK_PRIOR://pageup
	case VK_NEXT://pagedown
	case VK_CLEAR:
	case VK_KANA:
	case VK_JUNJA:
	case VK_FINAL: 
	case VK_HANJA: 
	case VK_CONVERT:
	case VK_NONCONVERT:
	case VK_ACCEPT: 
	case VK_MODECHANGE:
	case VK_SELECT:
	case VK_PRINT:
	case VK_EXECUTE:
	*/
	case VK_LEFT:
	case VK_RIGHT:
	case VK_RETURN:
	case VK_BACK:
	case VK_SPACE:
	case VK_DOWN:
	case VK_UP:
	case VK_TAB:
	case VK_HOME:
	case VK_END:
	case VK_PRIOR://pageup
	case VK_NEXT://pagedown
	case VK_OEM_5:///
	case VK_OEM_PLUS://+
	case VK_OEM_MINUS://-

	//case VK_CONTROL:
	//case VK_OEM_3://`
		if (_IsComposing())
			return TRUE;
		return FALSE;
	case VK_ESCAPE:
	case VK_OEM_4://[
	case VK_OEM_6://]
	case VK_LWIN:
	case VK_RWIN:
	case VK_APPS:
	case VK_OEM_3://`
	//case VK_CONTROL:
		//case VK_LBUTTON:
		//case MK_LBUTTON:
		//case WM_LBUTTONDOWN:
		return TRUE;
	default:
		if (_IsComposing())
			return TRUE;
		return FALSE;
	}




	
/*

	// Otherwise, we will eat any keys that produce a Unicode character
	WCHAR buf[16]; 
	UINT wScanCode = (lParam & 0x0000ff00) >> 8;
	int numChars = ToUnicodeEx((UINT)wParam, wScanCode, state, (LPWSTR)buf, 16, 0, 0);
	BOOL fEatKey = (numChars > 0);

	return fEatKey;
	*/
}


//+---------------------------------------------------------------------------
//
// OnSetFocus
//
// Called by the system whenever this service gets the keystroke device focus.
//----------------------------------------------------------------------------

STDAPI CTextService::OnSetFocus(BOOL fForeground)
{
	#ifdef _DEBUG
	DebugPrintf("STDAPI CTextService::OnSetFocus(BOOL fForeground)");
    #endif
	return S_OK;
}

//+---------------------------------------------------------------------------
//
// OnTestKeyDown
//
// Called by the system to query this service wants a potential keystroke.
//----------------------------------------------------------------------------

STDAPI CTextService::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
	#ifdef _DEBUG
	DebugPrintf("STDAPI CTextService::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)");
    #endif
	*pfEaten = _IsKeyEaten(wParam, lParam);
	return S_OK;
}

//+---------------------------------------------------------------------------
//
// OnKeyDown
//
// Called by the system to offer this service a keystroke.  If *pfEaten == TRUE
// on exit, the application will not handle the keystroke.
//----------------------------------------------------------------------------

STDAPI CTextService::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
	#ifdef _DEBUG
	DebugPrintf("STDAPI CTextService::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)");
    #endif
	
	BOOL fOpen;
	switch (wParam)
	{
	case VK_CONTROL :
		{
			#ifdef _DEBUG
			DebugPrintf("CONTROL_FLAG = TRUE;");
			#endif
			return S_OK;
		}
	case VK_SHIFT :
		{
		fOpen = _IsKeyboardOpen();
		_SetKeyboardOpen(FALSE);
		clocker_for_shift = clock();//counter start when shift start
		return S_OK;
		}
	default:
		*pfEaten = _IsKeyEaten(wParam, lParam);
		if (*pfEaten)
		{
			_InvokeKeyHandler(pContext, wParam, lParam);
		}
		return S_OK;	
	}
	
	/*
	
	*pfEaten = _IsKeyEaten(wParam, lParam);

	if (*pfEaten)
	{
	_InvokeKeyHandler(pContext, wParam, lParam);
	}
	return S_OK;
	*/
}

//+---------------------------------------------------------------------------
//
// OnTestKeyUp
//
// Called by the system to query this service wants a potential keystroke.
//----------------------------------------------------------------------------

STDAPI CTextService::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
	#ifdef _DEBUG
	DebugPrintf("STDAPI CTextService::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)");
    #endif
	*pfEaten = _IsKeyEaten(wParam, lParam);
	return S_OK;
}

//+---------------------------------------------------------------------------
//
// OnKeyUp
//
// Called by the system to offer this service a keystroke.  If *pfEaten == TRUE
// on exit, the application will not handle the keystroke.
//----------------------------------------------------------------------------

STDAPI CTextService::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)
{
	#ifdef _DEBUG
	DebugPrintf("STDAPI CTextService::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten)");
    #endif
	
	BOOL fOpen;
	switch (wParam)
	{
	case VK_CONTROL :
		{
			#ifdef _DEBUG
			DebugPrintf("CONTROL_FLAG = FALSE;");
			#endif
			return S_OK;
		}
	case VK_SHIFT :
		fOpen = _IsKeyboardOpen();
		_SetKeyboardOpen(TRUE);
		/*
		if(((clock()-clocker_for_shift)/CLOCKS_PER_SEC)<0.001)
		{
			if(TLphingim_enable==1)
			{
				TLphingim_enable=0;
			}
			else if(TLphingim_enable==0)
			{
				TLphingim_enable=1;
			}
			//POJ_enable=1;
		}
		*/
		return S_OK;
	case VK_OEM_3 :
		/*
		if(_IsKeyboardOpen())
		{
			if(TLphingim_enable==1)
			{
				TLphingim_enable=0;
			}
			else if(TLphingim_enable==0)
			{
				TLphingim_enable=1;
			}
		}
		*/
		return S_OK;
	default:
		*pfEaten = _IsKeyEaten(wParam, lParam);
		return S_OK;
	}
	
	/*
	*pfEaten = _IsKeyEaten(wParam, lParam);
	return S_OK;
	*/
	
}



//+---------------------------------------------------------------------------
//
// _InitKeyEventSink
//
// Advise a keystroke sink.
//----------------------------------------------------------------------------

BOOL CTextService::_InitKeyEventSink()
{
	#ifdef _DEBUG
	DebugPrintf("BOOL CTextService::_InitKeyEventSink()");
    #endif
	ITfKeystrokeMgr *pKeystrokeMgr;
	HRESULT hr;

	if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK)
		return FALSE;

	hr = pKeystrokeMgr->AdviseKeyEventSink(_tfClientId, (ITfKeyEventSink *)this, TRUE);

	pKeystrokeMgr->Release();


	return (hr == S_OK);
}

//+---------------------------------------------------------------------------
//
// _UninitKeyEventSink
//
// Unadvise a keystroke sink.  Assumes a sink has benn advised already.
//----------------------------------------------------------------------------

void CTextService::_UninitKeyEventSink()
{
	#ifdef _DEBUG
	DebugPrintf("void CTextService::_UninitKeyEventSink()");
    #endif
	ITfKeystrokeMgr *pKeystrokeMgr;

	if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK)
		return;

	pKeystrokeMgr->UnadviseKeyEventSink(_tfClientId);

	pKeystrokeMgr->Release();
}

//+---------------------------------------------------------------------------
//
// _InitKeyboard
//
// Open the keyboard for use
//----------------------------------------------------------------------------

BOOL CTextService::_InitKeyboard()
{
	#ifdef _DEBUG
	DebugPrintf("BOOL CTextService::_InitKeyboard()");
    #endif
	HRESULT hr = _SetKeyboardOpen(TRUE);
	return (hr == S_OK);
}

// _InitPreservedKey
// Register a hot key.
BOOL CTextService::_InitPreservedKey() 
{
		#ifdef _DEBUG
		DebugPrintf("BOOL CTextService::_InitPreservedKey() ");
		#endif
		ITfKeystrokeMgr *pKeystrokeMgr;
		HRESULT hr;

		if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK)
			return FALSE;

		// register Alt+~ key
		hr = pKeystrokeMgr->PreserveKey(_tfClientId, 
			GUID_PRESERVEDKEY_COMMON,
			&c_pkeyCommon,
			c_szPKeyCommon,
			wcslen(c_szPKeyCommon));

		hr = pKeystrokeMgr->PreserveKey(_tfClientId, 
			GUID_PRESERVEDKEY_PERIOD,
			&c_pkeyPeriod,
			c_szPKeyPeriod,
			wcslen(c_szPKeyPeriod));


		pKeystrokeMgr->Release();

		return (hr == S_OK);
}

// _UninitPreservedKey
// Uninit a hot key.
void CTextService::_UninitPreservedKey() 
{
		#ifdef _DEBUG
		DebugPrintf("void CTextService::_UninitPreservedKey() ");
		#endif
		ITfKeystrokeMgr *pKeystrokeMgr;

		if (_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK)
			return;

		pKeystrokeMgr->UnpreserveKey(GUID_PRESERVEDKEY_COMMON, &c_pkeyCommon);
		pKeystrokeMgr->UnpreserveKey(GUID_PRESERVEDKEY_PERIOD, &c_pkeyPeriod);

		pKeystrokeMgr->Release();
}



//+---------------------------------------------------------------------------
//
// OnPreservedKey
//
// Called when a hotkey (registered by us, or by the system) is typed.
//----------------------------------------------------------------------------

STDAPI CTextService::OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pfEaten)
{
	#ifdef _DEBUG
	DebugPrintf("STDAPI CTextService::OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pfEaten)");
    #endif
	WPARAM wParam;
	if (IsEqualGUID(rguid, GUID_PRESERVEDKEY_COMMON)) 
	{
		wParam=VK_OEM_COMMA;
		InsertCommon(wParam);
		//_InsertCharacter
		*pfEaten = TRUE;
		
	}
	else if (IsEqualGUID(rguid, GUID_PRESERVEDKEY_PERIOD))
	{
		wParam=VK_OEM_PERIOD;
		InsertCommon(wParam);
		*pfEaten = TRUE;
	}
	else 
	{
		*pfEaten = FALSE;
	}

	return S_OK;
 
	//*pfEaten = FALSE;
	//return S_OK;

}